use std::net::TcpStream;

#[allow(dead_code)]
pub struct Connection1 {
    pub name: String,
    pub stream: TcpStream,
}

#[allow(dead_code)]
pub struct Request {
    id: String,
    r#type: RequestType,
    payload: String,
}

#[allow(dead_code)]
pub enum RequestType {
    Get,
    Post,
}

#[allow(dead_code)]
pub struct Status {
    code: String,
}

#[allow(dead_code)]
pub struct SendErr {}

impl Request {
    pub fn new(id: String, r#type: RequestType, payload: String) -> Self {
        Request {
            id,
            r#type,
            payload,
        }
    }
}

impl Connection1 {
    /// Sends a request over the connection.
    ///
    /// # Example
    /// ```no_run
    /// # // Boilerplate are required to get an example working.
    /// # use std::net::TcpStream;
    /// # use easy_doc_initialization::{Connection1, Request, RequestType};
    /// #
    /// # let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
    /// # let connection = Connection1 { name: "foo".to_string(), stream };
    /// # let request = Request::new("RequestId".to_string(), RequestType::Get, "payload".to_string());
    /// # let response = connection.send_request(request);
    /// # assert!(response.is_ok());
    /// ```
    #[allow(dead_code)]
    pub fn send_request(&self, _request: Request) -> Result<Status, SendErr> {
        // ...
        Ok(Status {
            code: "ok".to_string(),
        })
    }

    /// Oh no, all that boilerplate needs to be repeated here!
    #[allow(dead_code)]
    fn check_status(&self) -> Status {
        // ...
        Status {
            code: "ok".to_string(),
        }
    }
}

#[allow(dead_code)]
pub struct Connection2 {
    name: String,
    stream: TcpStream,
}

// Instead of typing all of this boilerplate to create a Connection1 and Request,
// it is easier to just create a wrapping helper function which takes them as
// arguments.
//
// Note in the below example the line assert!(response.is_ok()); will not
// actually run while testing because it is inside a function which is never
// invoked.
impl Connection2 {
    /// Sends a request over the connection.
    ///
    /// # Example
    /// ```
    /// # use easy_doc_initialization::{Connection2, Request};
    /// #
    /// # fn call_send(connection: Connection2, request: Request) {
    /// # let response = connection.send_request(request);
    /// # assert!(response.is_ok());
    /// # }
    /// ```
    #[allow(dead_code)]
    pub fn send_request(&self, _request: Request) -> Result<Status, SendErr> {
        // ...
        Ok(Status {
            code: "ok".to_string(),
        })
    }
}
